feat(memtrack): collect RSS via rss_stat and folio-rmap reconstruction#453
feat(memtrack): collect RSS via rss_stat and folio-rmap reconstruction#453not-matthias wants to merge 15 commits into
Conversation
Merging this PR will not alter performance
|
Greptile SummaryThis PR adds RSS collection and rmap-based RSS reconstruction to memtrack. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (16): Last reviewed commit: "ci: slim bpf-tests kernel diagnostic to ..." | Re-trigger Greptile |
b670b0a to
2f41984
Compare
41945a5 to
64688a9
Compare
b02f3cb to
411b713
Compare
4d3fbb0 to
a7e1666
Compare
Sample the kernel's per-mm resident counter through the kmem:rss_stat tracepoint, emitting absolute byte values per mm member. Adds the EVENT_TYPE_RSS contract, MemtrackEventKind::Rss, the parser arm, and a writer bench case. An rss_stat update from reclaim or another process's madvise fires in the actor's context; track (mm_id, member) -> owning pid so those updates reach the owner. External events may only lower a counter, so stale reads and mm_id collisions cannot invent peaks.
Attach fentry hooks on the folio-rmap add/remove functions, emitting signed page-count deltas per MM_* bucket so anon, file, and shmem RSS can be reconstructed over time. Gated behind CODSPEED_MEMTRACK_TRACK_RMAP; the programs stay autoload-off by default so the skeleton loads on any kernel. Adds the EVENT_TYPE_RMAP contract, MemtrackEventKind::Rmap, parser arm, and bench case.
A forked child's inherited RSS is invisible to rss_stat: the fork-time counter copies fire outside the child's context, and anon COW faults are counter-neutral, so a child that only touches inherited memory never reports anything on its own. A fork event carrying the parent pid lets consumers seed the child from the parent's last absolutes; exec and exit mark where the address space is replaced or torn down.
Nine fixtures compare three per-process views - the fixture's own /proc report, rss_stat peaks, and rmap-reconstructed totals - plus an external-reclaim fixture proving out-of-context decrements reach the owner. Fork-seeded children are validated via fork_idle, whose 32 MiB is observable only through the fork-event seed. Pids are redacted and rows keep first-activity order so snapshots are stable across runs; fixtures report VmHWM instead of ru_maxrss, which survives execve and would leak the harness's peak RSS.
411b713 to
3037b25
Compare
e00fb6b to
a68424a
Compare
| self.attach_task_newtask()?; | ||
| self.attach_sched_process_exec()?; | ||
| self.attach_sched_process_exit()?; |
There was a problem hiding this comment.
Lifecycle Attaches Still Abort
These RSS lifecycle tracepoints still return errors from attach_tracepoints(), while rss_stat itself only warns and continues. If a host can attach the existing allocator probes but cannot attach task:task_newtask, sched:sched_process_exec, or sched:sched_process_exit, tracker startup still fails before allocator tracking begins. These tracepoints only support RSS fork/exec/exit reconciliation, so unsupported RSS lifecycle accounting can still make memory tracking unavailable.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/memtrack/src/ebpf/memtrack/tracking.rs
Line: 54-56
Comment:
**Lifecycle Attaches Still Abort**
These RSS lifecycle tracepoints still return errors from `attach_tracepoints()`, while `rss_stat` itself only warns and continues. If a host can attach the existing allocator probes but cannot attach `task:task_newtask`, `sched:sched_process_exec`, or `sched:sched_process_exit`, tracker startup still fails before allocator tracking begins. These tracepoints only support RSS fork/exec/exit reconciliation, so unsupported RSS lifecycle accounting can still make memory tracking unavailable.
How can I resolve this? If you propose a fix, please make it concise.| self.attach_sched_fork()?; | ||
| self.attach_task_newtask()?; | ||
| self.attach_sched_process_exec()?; |
There was a problem hiding this comment.
These RSS lifecycle tracepoints still propagate attach errors from attach_tracepoints(). Tracker::new() calls that method during normal memory tracking startup, so a host that can run the existing allocator probes but cannot attach task:task_newtask, sched:sched_process_exec, or sched:sched_process_exit will still fail before allocator tracking starts. Since these tracepoints only support RSS reconciliation, their attach failures should not make the whole tracker unavailable.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/memtrack/src/ebpf/memtrack/tracking.rs
Line: 53-55
Comment:
**Lifecycle attaches abort**
These RSS lifecycle tracepoints still propagate attach errors from `attach_tracepoints()`. `Tracker::new()` calls that method during normal memory tracking startup, so a host that can run the existing allocator probes but cannot attach `task:task_newtask`, `sched:sched_process_exec`, or `sched:sched_process_exit` will still fail before allocator tracking starts. Since these tracepoints only support RSS reconciliation, their attach failures should not make the whole tracker unavailable.
How can I resolve this? If you propose a fix, please make it concise.e48a0a0 to
f305358
Compare
| self.attach_task_newtask()?; | ||
| self.attach_sched_process_exec()?; | ||
| self.attach_sched_process_exit()?; |
There was a problem hiding this comment.
Lifecycle Attaches Still Abort
These RSS lifecycle tracepoints still return attach errors from attach_tracepoints(). Tracker::new() and new_without_allocators_with_rmap() both call this method during startup, so a host that can run the allocator probes but cannot attach task:task_newtask, sched:sched_process_exec, or sched:sched_process_exit will still fail before memory tracking starts. These events only support RSS reconciliation, so their attach failures should disable that RSS lifecycle path rather than make all memtrack modes unavailable.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/memtrack/src/ebpf/memtrack/tracking.rs
Line: 54-56
Comment:
**Lifecycle Attaches Still Abort**
These RSS lifecycle tracepoints still return attach errors from `attach_tracepoints()`. `Tracker::new()` and `new_without_allocators_with_rmap()` both call this method during startup, so a host that can run the allocator probes but cannot attach `task:task_newtask`, `sched:sched_process_exec`, or `sched:sched_process_exit` will still fail before memory tracking starts. These events only support RSS reconciliation, so their attach failures should disable that RSS lifecycle path rather than make all memtrack modes unavailable.
How can I resolve this? If you propose a fix, please make it concise.|
@greptileai pause reviews for now until i enable them again |
|
Understood — pausing reviews. I'll hold off until you re-enable them. Tip: You can customize Greptile's behavior for this repo with |
| self.attach_task_newtask()?; | ||
| self.attach_sched_process_exec()?; | ||
| self.attach_sched_process_exit()?; |
There was a problem hiding this comment.
Lifecycle Attaches Still Abort
These RSS lifecycle tracepoints still return errors from attach_tracepoints(), while rss_stat itself only warns and continues. Tracker::new() and new_without_allocators_with_rmap() call this during startup, so a host that can run the allocator probes but cannot attach task:task_newtask, sched:sched_process_exec, or sched:sched_process_exit still fails before memory tracking starts. These tracepoints only support RSS lifecycle reconciliation, so their attach failures should disable that RSS lifecycle path instead of taking down all memtrack startup.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/memtrack/src/ebpf/memtrack/tracking.rs
Line: 54-56
Comment:
**Lifecycle Attaches Still Abort**
These RSS lifecycle tracepoints still return errors from `attach_tracepoints()`, while `rss_stat` itself only warns and continues. `Tracker::new()` and `new_without_allocators_with_rmap()` call this during startup, so a host that can run the allocator probes but cannot attach `task:task_newtask`, `sched:sched_process_exec`, or `sched:sched_process_exit` still fails before memory tracking starts. These tracepoints only support RSS lifecycle reconciliation, so their attach failures should disable that RSS lifecycle path instead of taking down all memtrack startup.
How can I resolve this? If you propose a fix, please make it concise.
Summary
Adds RSS (resident set size) collection to
memtrack, in two layers:kmem:rss_stattracepoint — absolute per-mm resident bytes (anon/file/shmem/swap), latest-wins.fentryhooks on the anon folio-rmap add/remove functions emit signed page-count deltas, so anon RSS can be rebuilt over time asΣ(add − remove) × PAGE_SIZE.The reconstruction is a total anon RSS delta-sum, not a per-vaddr resident map — the kernel remove hook (
folio_remove_rmap_ptes) carries no address, so removals can't be attributed to a vaddr (the add hooks' faulting vaddr is emitted for observability only).Commits
feat(memtrack): track RSS via kmem:rss_stat tracepoint— the baseline:EVENT_TYPE_RSScontract,MemtrackEventKind::Rss, parser arm, writer bench case, gated integration test.feat(memtrack): reconstruct anon RSS from gated folio rmap fentry hooks—EVENT_TYPE_RMAP_ANON+RmapAnonevent, fivefentryprograms (add_new / add_ptes / remove_ptes / remove_pmd / remove_pud), CO-RE folio helpers, and the load/attach gating.test(memtrack): validate anon RSS reconstruction against rss_stat— ramps anon RSS viammap/munmapand asserts the reconstructed estimate tracks therss_statMM_ANONPAGES peak within 25%.What's on by default vs gated
rss_stattracepoint: always on. EmittingRssevents is the intended new default behavior introduced by this change — the RSS tracepoint is not gated.RmapAnonfolio-rmapfentryprograms: off by default, gated behindCODSPEED_MEMTRACK_TRACK_RMAP=1. When the flag is unset they areset_autoload(false)before load and never attached, so:fentryBTF target would otherwise fail the whole load), and--mode memory) and out of the existing test suites — noRmapAnonevents are produced by default.Verification
Run in a privileged,
--pid=hostcontainer sharing the host kernel (7.0.12):real anon amplitude = 64 MiB, estimated peak = 64 MiB(ratio 1.00).rss_tests, flag unset): ✅ passes — noRmapAnonevents, folio-rmap programs stay unloaded.cargo fmt, andclippyclean.folio_*_rmap*functions verified to match theBPF_PROGarg layouts.Review notes (draft)
track_commandordering: the shared test helper spawns the child beforeenable()/track(root_pid). In practice the child'sfork→execve→ld.so→libc-initfar outlasts the two BPF-map updates, so tracking is armed before the workload allocates (both fixtures captured full event streams). Flagging in case we'd prefer a leading settle-usleepin the fixtures or an enable-before-spawn change in the helper.PAGE_SIZE: hardcoded to 4096 (correct on x86_64). On a 16K/64K-page arm64 runner the estimate would needsysconf(_SC_PAGESIZE);rss_statis already in bytes and unaffected. Happy to switch tosysconfif these tests run on arm64 CI.